using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Text;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading;

using AMPLibrary.Collections;
using AMPLibrary.Controls;

namespace AMPLibrary.Media
{
    /// <summary>
    /// 
    /// </summary>
    /// <param name="source"></param>
    /// <param name="e"></param>
    public delegate void MediaFailedEventHandler(object source, EventArgs e);

    /// <summary>
    /// 
    /// </summary>
    /// <param name="source"></param>
    /// <param name="e"></param>
    public delegate void MediaEndedEventHandler(object source, EventArgs e);
    
    /// <summary>
    /// 
    /// </summary>
    public class MediaPlayer
    {
        //
        private NowPlayingCollection nowPlayingCollection = NowPlayingCollection.GetNowPlayingCollection();

        //
        private Random random = new Random(Convert.ToInt32(DateTime.Now.Second));

        //
        private static MediaPlayer mediaPlayer;

        //
        private MediaFile currentMedia;

        //
        private MediaElement mediaElement;

        //
        private Label currentMediaLabel;

        //
        private MusicMediaInformationControl musicMediaInformationControl;

        //
        private DispatcherTimer MediaSliderTimer;

        //
        private double currentStopTime;

        //
        private MediaFile currentStopMedia;

        //
        public event MediaEndedEventHandler OnMediaEnded;

        //
        public event MediaFailedEventHandler OnMediaFailed;

        //
        private Dispatcher dispatcher = Dispatcher.CurrentDispatcher;

        //
        public event PropertyChangedEventHandler PropertyChanged = delegate { };

        //
        private TimeSpan _currentPositionTimeSpan;

        //
        private TimeSpan _totalPlayTimeTimeSpan;

        /// <summary>
        /// 
        /// </summary>
        public TimeSpan CurrentPlayTimeTimeSpan
        {
            get
            {
                return _currentPositionTimeSpan;
            }
            private set
            {
                _currentPositionTimeSpan = value;
                OnPropertyChanged("CurrentPlayTimeTimeSpan");
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public TimeSpan TotalPlayTimeTimeSpan
        {
            get
            {
                return _totalPlayTimeTimeSpan;
            }
            private set
            {
                _totalPlayTimeTimeSpan = value;
                OnPropertyChanged("TotalPlayTimeTimeSpan");
            }
        }
        
        /// <summary>
        /// 
        /// </summary>
        public MusicMediaInformationControl AudioMediaInformationControl
        {
            get
            {
                return this.musicMediaInformationControl;
            }
            private set
            {
                this.musicMediaInformationControl = value;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public MediaPlayerState CurrentMediaPlayerState
        {
            get;
            set;
        }

        /// <summary>
        /// 
        /// </summary>
        public MediaFile CurrentMedia
        {
            get
            {
                return this.currentMedia;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public MediaElement MediaElement
        {
            get
            {
                return this.mediaElement;
            }
            private set
            {
                this.mediaElement = value;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public bool Repeat
        {
            get
            {
                return Settings.Repeat;
            }
            set
            {
                Settings.Repeat = value;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public bool Random
        {
            get
            {
                return Settings.Random;
            }
            set
            {
                Settings.Random = value;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public int CurrentlySelectedIndex
        {
            get;
            set;
        }

        /// <summary>
        /// 
        /// </summary>
        public NowPlayingCollection NowPlayingCollection
        {
            get
            {
                return this.nowPlayingCollection;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        private MediaPlayer()
        {
            // set up the visual elements
            this.mediaElement = new MediaElement();
            this.currentMediaLabel = new Label();
            this.musicMediaInformationControl = new MusicMediaInformationControl();

            // set up the AMP properties
            this.CurrentlySelectedIndex = -1;

            // set up the events
            nowPlayingCollection.ItemRemoved += new ChangedEventHandler(nowPlayingCollection_ItemRemoved);
            nowPlayingCollection.ItemsRemoved += new ChangedEventHandler(nowPlayingCollection_ItemsRemoved);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public static MediaPlayer GetMediaPlayer()
        {
            if (mediaPlayer == null)
                mediaPlayer = new MediaPlayer();

            return mediaPlayer;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public static MediaPlayer GetMediaPlayer(ref ContentPresenter mediaElement, ref ContentPresenter mediaInformationContentPresenter)
        {
            if (mediaPlayer == null)
                mediaPlayer = new MediaPlayer();

            mediaPlayer.MediaElement.UnloadedBehavior = MediaState.Manual;
            mediaPlayer.MediaElement.LoadedBehavior = MediaState.Manual;
            mediaPlayer.MediaElement.MediaEnded += new System.Windows.RoutedEventHandler(mediaPlayer.MediaEnded);
            mediaPlayer.MediaElement.MediaFailed += new EventHandler<ExceptionRoutedEventArgs>(mediaPlayer.MediaFailed);
            mediaElement.Content = mediaPlayer.MediaElement;

            mediaInformationContentPresenter.Content = mediaPlayer.musicMediaInformationControl;
            
            return mediaPlayer;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public static MediaPlayer GetMediaPlayer(ref ContentPresenter mediaElement)
        {
            if (mediaPlayer == null)
                mediaPlayer = new MediaPlayer();

            mediaPlayer.MediaElement.UnloadedBehavior = MediaState.Manual;
            mediaPlayer.MediaElement.LoadedBehavior = MediaState.Manual;
            mediaPlayer.MediaElement.MediaEnded += new System.Windows.RoutedEventHandler(mediaPlayer.MediaEnded);
            mediaPlayer.MediaElement.MediaFailed += new EventHandler<ExceptionRoutedEventArgs>(mediaPlayer.MediaFailed);
            mediaElement.Content = mediaPlayer.MediaElement;

            return mediaPlayer;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="propertyName"></param>
        protected void OnPropertyChanged(string propertyName)
        {
            if (dispatcher.Thread == Thread.CurrentThread)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            else
                dispatcher.BeginInvoke(DispatcherPriority.DataBind, (ThreadStart)delegate() { PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); });
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void MediaFailed(object sender, ExceptionRoutedEventArgs e)
        {
            Stop();

            // roll back the play attempt
            if(this.currentMedia != null)
                this.currentMedia.DeIncrimentPlayCount();

            // when media failed has been fired from the canvas
            // fire my own event for media ended
            OnMediaFailed(sender, e);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void MediaEnded(object sender, System.Windows.RoutedEventArgs e)
        {
            Stop();

            // when media ended has been fired from the canvas
            // fire my own event for media ended
            OnMediaEnded(sender, e);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public bool Play(ref MediaFile media)
        {
            if (!media.Exists())
            {
                Stop();
                this.currentMedia = null;
                return false;
            }

            try
            {
                // stop anything that was playing
                Stop();

                this.currentMedia = MediaFile.LoadMedia(media.Path);

                Uri mediaUri = new Uri(this.currentMedia.Path);

                MediaElement.Source = mediaUri;
                MediaElement.Play();

                media.SetLastPlayed();
                media.IncrimentPlayCount();
                
                // wait for the media to load
                while (this.MediaElement.NaturalDuration.HasTimeSpan == false)
                    ;
                
                this.TotalPlayTimeTimeSpan = MediaElement.NaturalDuration.TimeSpan;

                this.MediaSliderTimer = new DispatcherTimer();
                this.MediaSliderTimer.Tick += new EventHandler(timelineSliderTimer_Tick);
                this.MediaSliderTimer.Interval = new TimeSpan(0, 0, 0, 0, 10);
                this.MediaSliderTimer.Start();

                this.CurrentMediaPlayerState = MediaPlayerState.Playing;

                if (media.MimeType == MimeTypes.MP3)
                {
                    musicMediaInformationControl.SetAudioMediaInformation(media);
                    musicMediaInformationControl.Visibility = Visibility.Visible;
                }
                else if (media.MimeType == MimeTypes.AVI)
                {
                    musicMediaInformationControl.Visibility = Visibility.Hidden;
                }
            }
            catch
            {
                this.currentMedia = null;
                return false;
            }

            return true;
        }
        
        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public bool Next()
        {
            if (nowPlayingCollection.Count == 0)
            {
                CurrentlySelectedIndex = -1;
                currentMedia = null;
                return false;
            }
            
            int nextIndex = CurrentlySelectedIndex;

            if (Settings.Repeat == true && Settings.Random == true)
            {
                while (nextIndex == CurrentlySelectedIndex)
                    nextIndex = random.Next(0, nowPlayingCollection.Count);
            }
            else if (Settings.Repeat == true && Settings.Random == false)
            {
                nextIndex++;

                if (nextIndex > this.nowPlayingCollection.Count - 1)
                    nextIndex = 0;
            }
            else if (Settings.Repeat == false && Settings.Random == true)
            {
                while (nextIndex == CurrentlySelectedIndex)
                    nextIndex = random.Next(0, nowPlayingCollection.Count);
            }
            else if (Settings.Repeat == false && Settings.Random == false)
            {
                nextIndex++;

                if (nextIndex > this.nowPlayingCollection.Count - 1)
                {
                    Stop();
                    return false;
                }
            }

            CurrentlySelectedIndex = nextIndex;

            MediaFile media = nowPlayingCollection.GetItem(nextIndex);
            if (media == null)
                return false;
            
            if (Settings.Random == true)
            {
                RandomQueue randomQueue = RandomQueue.GetRandomQueue();

                // if it is int he queue fail, get another song
                if (randomQueue.IsInQueue(media.Path))
                {
                    // if the queue size is the size of the playlist clear it to allow new songs to be played
                    if (randomQueue.CurrentCapacity != nowPlayingCollection.Count)
                    {
                        //randomFail++;
                        Next();
                        return false;
                    }
                    else
                        randomQueue.Clear();
                }
                else
                    randomQueue.Insert(media.Path);
            }

            return Play(ref media);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public bool Previous()
        {
            if (nowPlayingCollection.Count == 0)
            {
                CurrentlySelectedIndex = -1;
                currentMedia = null;
                return false;
            }

            Random random = new Random(DateTime.Now.Second);
            int nextIndex = CurrentlySelectedIndex;

            if (Settings.Repeat == true && Settings.Random == true)
            {
                while (nextIndex == CurrentlySelectedIndex)
                    nextIndex = random.Next(0, nowPlayingCollection.Count);
            }
            else if (Settings.Repeat == true && Settings.Random == false)
            {
                nextIndex--;

                if (nextIndex < 0)
                    nextIndex = nowPlayingCollection.Count-1;
            }
            else if (Settings.Repeat == false && Settings.Random == true)
            {
                while (nextIndex == CurrentlySelectedIndex)
                    nextIndex = random.Next(0, nowPlayingCollection.Count);
            }
            else if (Settings.Repeat == false && Settings.Random == false)
            {
                nextIndex--;

                if(nextIndex < 0)
                {
                    CurrentlySelectedIndex = nextIndex;
                    Stop();
                    return false;
                }
            }

            CurrentlySelectedIndex = nextIndex;

            MediaFile media = nowPlayingCollection.GetItem(nextIndex);
            if (media == null)
                return false;

            if (Settings.Random == true)
            {
                RandomQueue randomQueue = RandomQueue.GetRandomQueue();

                // if it is int he queue fail, get another song
                if (randomQueue.IsInQueue(media.Path))
                {
                    // if the queue size is the size of the playlist clear it to allow new songs to be played
                    if (randomQueue.CurrentCapacity != nowPlayingCollection.Count)
                    {
                        //randomFail++;
                        Previous();
                        return false;
                    }
                    else
                        randomQueue.Clear();
                }
                else
                    randomQueue.Insert(media.Path);
            }

            return Play(ref media);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public void Stop()
        {
            try
            {
                this.MediaElement.Stop();
                this.MediaElement.Close();
                this.MediaElement.Source = null;
            }
            catch
            {
            }

            try
            {
                this.MediaSliderTimer.Stop();
                this.MediaSliderTimer = null;
            }
            catch
            {
            }

            this.CurrentMediaPlayerState = MediaPlayerState.Stopped;
            this.currentMedia = null;

            this.musicMediaInformationControl.Clear();
            this.musicMediaInformationControl.Visibility = Visibility.Visible;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public void StopForUpdate()
        {
            this.currentStopTime = MediaElement.Position.TotalMilliseconds;

            try
            {
                this.MediaElement.Stop();
                this.MediaElement.Close();
                this.MediaElement.Source = null;
            }
            catch
            {
            }

            try
            {
                this.MediaSliderTimer.Stop();
                this.MediaSliderTimer = null;
            }
            catch
            {
            }

            this.CurrentMediaPlayerState = MediaPlayerState.Stopped;
            this.currentStopMedia = this.currentMedia;
            this.currentMedia = null;

            this.musicMediaInformationControl.Clear();
            this.musicMediaInformationControl.Visibility = Visibility.Visible;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public void ResumeFromUpdate()
        {
            if (this.currentStopMedia == null)
                return;

            Play(ref this.currentStopMedia);
            GotoTime(this.currentStopTime);
        }

        /// <summary>
        /// 
        /// </summary>
        public void Pause()
        {
            if (CurrentMediaPlayerState == MediaPlayerState.Paused)
            {
                this.MediaElement.Play();
                this.CurrentMediaPlayerState = MediaPlayerState.Playing;
            }
            else if (CurrentMediaPlayerState == MediaPlayerState.Playing)
            {
                this.MediaElement.Pause();
                this.CurrentMediaPlayerState = MediaPlayerState.Paused;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="value"></param>
        public void SetVolumn(double value)
        {
            mediaPlayer.mediaElement.Volume = value;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="second"></param>
        /// <returns></returns>
        public void GotoTime(double millsecondCount)
        {
            TimeSpan ts = new TimeSpan(0, 0, 0, 0, (int)millsecondCount);
            MediaElement.Position = ts;
        }

        /// <summary>
        /// 
        /// </summary>
        private void ResetIndex()
        {
            bool found = false;
            int i = 0;

            if (nowPlayingCollection.Count == 0)
            {
                this.CurrentlySelectedIndex = -1;
                return;
            }

            if (currentMedia == null)
            {
                this.CurrentlySelectedIndex = -1;
                return;
            }

            for (i = 0; i < nowPlayingCollection.Count; i++)
            {
                if (nowPlayingCollection.GetItem(i).Path == currentMedia.Path)
                {
                    found = true;
                    break;
                }
            }

            if (found)
                this.CurrentlySelectedIndex = i;
            else
                this.CurrentlySelectedIndex = -1;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void timelineSliderTimer_Tick(object sender, EventArgs e)
        {
            this.CurrentPlayTimeTimeSpan = MediaElement.Position;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void nowPlayingCollection_ItemRemoved(object sender, EventArgs e)
        {
            ResetIndex();
        }
        
        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void nowPlayingCollection_ItemsRemoved(object sender, EventArgs e)
        {
            ResetIndex();
        }
    }
}